#include "core/motion_state.hpp"

#include "object_manager.hpp"
#include "logger.hpp"
#include "res_manager.hpp"
#include "render.hpp"
#include "physics.hpp"

#include "f_engine.hpp"

#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>

using namespace std;

boost::shared_ptr<if_system_t> object_manager_t::create(f_engine_t *engine)
{
    boost::shared_ptr<object_manager_t> ret(new object_manager_t(engine));
    logger::log(DEBUG, "[Object Manager] Object Manager system created.");
    return ret;
}

object_manager_t::object_manager_t(f_engine_t *engine) :
    engine_(engine)
{
}

object_manager_t::~object_manager_t()
{
    logger::log(DEBUG, "[Object Manager] Object Manager system destruction...");

    BOOST_FOREACH(obj_map_t::value_type &i, objects_)
                {
                    delete_object_by_id(i.first);
                }
}

void object_manager_t::start()
{
    engine_->report_started();
}

boost::shared_ptr<object_t> object_manager_t::create_object(const std::string &class_name, const vector3_t &init_pos, const quaternion_t &init_dir)
{
    logger::log(DEBUG) << "[Object] Creating object of class " << class_name;

    boost::shared_ptr<object_t> ret(new object_t);

    ret->ID = object_t::gen_uid();

    string res_name("objects/");
    res_name += class_name;

    ret->class_ = engine_->system<res_manager_t> ().get_res<object_class_t> (res_name);

    boost::mutex::scoped_lock g(objects_sync_locker_);

    boost::tuple<boost::shared_ptr<scene_node_t>, vector3_t> entity = engine_->system<render_t> ().create_entity(*ret->class_);

    scalar_t per_physics_scale = engine_->system<render_t> ().get_per_phisics_scale();

    vector3_t box = entity.get<1>() / per_physics_scale;

    world_transform_t transform;

    transform.position = init_pos;
    transform.rotation = init_dir;

    ret->motion_.reset(new motion_state_t(transform, entity.get<0>()));

    engine_->system<physics_manager_t> ().add_simulation_object(ret->ID, ret->motion_, box, ret->class_->mass_);

    logger::log(DEBUG) << "[Object] Object of class " << class_name << " ID = " << ret->ID << " created";

    objects_[ret->ID] = ret;

    return ret;
}

bool object_manager_t::delete_object(boost::shared_ptr<object_t> object)
{
    return delete_object_by_id(object->ID);
}
bool object_manager_t::delete_object_by_id(boost::uint64_t ID)
{
    boost::mutex::scoped_lock g(objects_sync_locker_);

    obj_map_t::iterator i = objects_.find(ID);
    if (i != objects_.end())
    {
        engine_->system<physics_manager_t> ().remove_simulation_object(ID);
        objects_.erase(i);
        return true;
    }
    return false;
}
